<?php

namespace App\ServicesNew\Impl;

use App\Entities\CouponCodeEntity;
use App\Entities\CouponCodeModel;
use App\Entities\OrderEntity;
use App\Entities\UserCouponEntity;
use App\Models\UserCouponModel;
use App\ServicesNew\BaseSearchServiceTrait;
use App\ServicesNew\CouponService;
use App\Utils\Assert;
use App\VO\UserCoupon\MetaInfo\UserCouponSearchInfo;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class CouponServiceImpl implements CouponService
{
    use BaseSearchServiceTrait;

    /**
     * 缓存
     * @var array
     */
    protected static $couponCodeCacheById = [];

    /**
     * @param CouponCodeEntity $couponCodeEntity
     */
    protected function cacheCouponById(CouponCodeEntity $couponCodeEntity)
    {
        self::$couponCodeCacheById[$couponCodeEntity->getId()] = $couponCodeEntity;
    }

    /**
     * 从缓存获取
     * @param int $couponId
     * @return CouponCodeEntity|null
     */
    protected function getCouponFromCache(int $couponId)
    {
        return self::$couponCodeCacheById[$couponId] ?? null;
    }

    /**
     * @inheritDoc
     */
    public function findCouponEntityById(int $couponId)
    {
        $couponCodeEntity = $this->getCouponFromCache($couponId);
        if ($couponCodeEntity instanceof CouponCodeEntity) {
            return $couponCodeEntity;
        }

        $couponCodeEntity = CouponCodeEntity::find($couponId);
        if ($couponCodeEntity instanceof CouponCodeEntity) {
            $this->cacheCouponById($couponCodeEntity);
        }

        return $couponCodeEntity;
    }

    /**
     * @inheritDoc
     */
    public function findCouponEntityByIdAndUserId(int $couponId, int $userId)
    {
        $userCouponEntity = $this->findUserCouponEntityByCouponIdAndUserId($couponId, $userId);
        if (is_null($userCouponEntity)) {
            return null;
        }

        return $this->findCouponEntityById($userCouponEntity->getCouponId());
    }

    /**
     * @inheritDoc
     */
    public function orderUseCouponByCouponId(OrderEntity $orderEntity, int $couponId)
    {
        $couponCodeEntity = CouponCodeEntity::find($couponId);
        $this->orderUseCouponByCouponEntity($orderEntity, $couponCodeEntity);
    }

    /**
     * @inheritDoc
     */
    public function orderUseCouponByCouponEntity(OrderEntity $orderEntity, CouponCodeEntity $couponCodeEntity)
    {
        DB::beginTransaction();

        // 1. 修改 coupon 状态
        $couponCodeEntity->setUsed();
        $couponCodeEntity->save();

        // 2. 修改 user_coupon 状态

        DB::commit();
    }

    /**
     * @inheritDoc
     */
    public function getCouponEntities(UserCouponSearchInfo $searchInfo)
    {
        /** * @var UserCouponEntity[] $userCoupons */
        $userCoupons = $this->getEntities(UserCouponEntity::class, UserCouponModel::class, $searchInfo);

        $now = Carbon::now()->toDateTimeString();

        $result = [];
        foreach ($userCoupons as $userCoupon) {
            $couponCodeEntity = $this->findCouponEntityById($userCoupon->getCouponId());
            if (is_null($couponCodeEntity)) {
                continue;
            }

            // 查找使用过的优惠券
            $used = $searchInfo->getUsed();
            if ($used) {
                $result[] = $couponCodeEntity;
                continue;
            }

            // 查找过期的
            $expired = $searchInfo->getExpired();
            if (0 === $expired) {
                $notAfter = $couponCodeEntity->getNotAfter();
                if (empty($notAfter) || $notAfter >= $now) {
                    $result[] = $couponCodeEntity;
                }
            } elseif (1 == $expired){
                $notAfter = $couponCodeEntity->getNotAfter();
                if ($notAfter && $notAfter <= $now) {
                    $result[] = $couponCodeEntity;
                }
            } else {
                $result[] = $couponCodeEntity;
            }
        }

        return $result;
    }

    /**
     * @inheritDoc
     */
    public function count(UserCouponSearchInfo $searchInfo)
    {
        return $this->countNum(UserCouponModel::class, $searchInfo);
    }

    /**
     * @param UserCouponSearchInfo $searchInfo
     * @return array
     */
    private function getSearchCondition(UserCouponSearchInfo $searchInfo)
    {
        $condition = [];
        $userId = $searchInfo->getUserId();
        if (!empty($userId)) {
            $condition[] = ['user_id', $userId];
        }

        $used = $searchInfo->getUsed();
        if (!is_null($used)) {
            $condition[] = ['used', $used];
        }

        return $condition;
    }

    /**
     * @inheritDoc
     */
    public function getUseAbleCouponCodeListByUserId(int $userId)
    {
        $collection = UserCouponModel::where('used', '=', 0)
            ->where('user_id', '=', $userId)
            ->get();

        $couponIds = array_column($collection->toArray(), 'coupon_id');
        $collection = CouponCodeModel::whereIn('id', $couponIds)
            ->where('enabled', '=', CouponCodeModel::ENABLED)
            //->where('not_before', '<=', $date)
            ->where(function ($query) {
                $query->whereNull('not_after')
                    ->orWhere('not_after', '>=', Carbon::now()->toDateTimeString());
            })->get();

        $ret = [];
        foreach ($collection as $model) {
            $ret[] = new CouponCodeEntity($model);
        }
        return $ret;
    }

    /**
     * @inheritDoc
     */
    public function orderRollback(OrderEntity $orderEntity)
    {
        DB::beginTransaction();

        $couponCodeId = $orderEntity->getCouponCodeId();

        $couponCode = CouponCodeEntity::find($couponCodeId);
        Assert::notNull($couponCode);
        $couponCode->setUsed(CouponCodeModel::COUPON_UNUSED);
        $couponCode->save();

        $userCouponCodeEntity = $this->findUserCouponEntityByCouponIdAndUserId($couponCodeId, $orderEntity->getUserId());
        Assert::notNull($userCouponCodeEntity);
        $userCouponCodeEntity->setUsed(CouponCodeModel::COUPON_UNUSED);
        $userCouponCodeEntity->save();

        DB::commit();
        Log::info('订单' . $orderEntity->getId() . '使用的优惠券 ' . $couponCodeId . ' 回退成功');
    }

    /**
     * @inheritDoc
     */
    public function findUserCouponEntityByCouponIdAndUserId(int $couponId, int $userId)
    {
        $userCouponCode = UserCouponModel::where('user_id', '=', $userId)
            ->where('coupon_id', '=', $couponId)
            ->first();

        if (is_null($userCouponCode)) {
            return null;
        }

        return new UserCouponEntity($userCouponCode);
    }
}
